﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using System.Diagnostics;

namespace DirectX_CS
{
	public partial class Lighting : Form, INamed
	{
        protected Device mDevice = null;
        protected PrimitiveManager mRenderer = null;
		private static Lighting onlyForm = null;
		private static Form mDirectXWindow = null;
        
        bool initializing = true;
		protected static List<LightCone> lightCones = null;
		private string shortName = "Lighting";
		[PGBrowsable]
		public string ShortName
		{
			get { return shortName; }
			set { shortName = value; }
		}
		private int shortNumber = 0;
		[PGBrowsable]
		public int ShortNumber
		{
			get { return shortNumber; }
			set { shortNumber = value; }
		}
		[PGBrowsable]
		public string ObjectName
		{
			get { return shortName + " " + shortNumber; }
		}
		public static Lighting OnlyForm
		{
			get { return onlyForm; }
		}
		public bool createLightCones = true;

        public Lighting(Device dXDevice, PrimitiveManager renderManager, Form mainDirectXWindow)
        {
			onlyForm = this;
			
            InitializeComponent();
			mDirectXWindow = mainDirectXWindow;
            mRenderer = renderManager;
            mDevice = dXDevice;
			
			initializing = true;//values will cause resets in other values if this value is not checked first
			LightingEnabled = LightingEnabled; //set check box to whatever lighting is

            //load the lights
            clbLights.Items.Clear();
            LightList lights = new LightList(mDevice);
            clbLights.DataSource = lights;
            clbLights.DisplayMember = "Text";
            clbLights.ValueMember = "LightIndex";
			int lIdx;

			for (lIdx = clbLights.Items.Count  - 1 ; lIdx >=0; lIdx--)
            {
				bool enabled = mDevice.Lights[lIdx].Enabled;
				//clbLights.SetItemChecked(lIdx, enabled);
				if (enabled)
				{
					//set selected, make sure by going backwards that light 0 is always selected if enabled.
					//and that a light is always selected if it is the only one enabled.
					clbLights.SelectedIndex = lIdx;
				}
            }

            //load the lights type combo box
            cboLightType.DataSource = new LightTypeList();
            cboLightType.DisplayMember = "sValue";
            cboLightType.ValueMember = "iValue";
			
            //select some data in the list and show it
            clbLights.Select();

			if (createLightCones)
			{
				//Create light cones.. (before calling load selected light)
				lightCones = new List<LightCone>();
				lIdx = 0;
				foreach (Light light in mDevice.Lights)
				{
					LightCone lc = new LightCone(lIdx);
					lightCones.Add(lc);
					lIdx++;
				}
				SetAllLightCones();
			}
			LoadSelectedLight();
            initializing = false;
        }

		private static void RecreateLightCone(int lightNumber)
		{
			if (lightCones != null)
			{
				LightCone nextCone = lightCones[lightNumber];
				nextCone.Recreate();
			}
		}
		public List<Light> GetUserSelectedLights()
		{
			List<Light> selectedLights = new List<Light>();
			foreach (LightListItem lli in clbLights.SelectedItems)
			{
				selectedLights.Add(lli.Light);
			}
			return selectedLights;
		}

        public void LoadSelectedLight()
        {
			initializing = true;
			int lightNumber = clbLights.SelectedIndex;
			if (lightNumber < 0)
				return;

			pgViewer.SelectedObjects = GetUserSelectedLights().Cast<object>().ToArray();

			this.Text = "Lighting (#" + lightNumber + " selected)";

			Light l = mDevice.Lights[lightNumber];

			//our data source and checked list box handles clbLights.Enabled 
			cboLightType.SelectedValue = (int)l.Type;

			//Reference: see Ch. 8.5. LIGHT SOURCES p. 263, from 1998 Mark A. Wicks
			//switch (l.Type)
			//{
			//    case LightType.Directional:
			//        pnlPosition.Enabled = false; 
			//        pnlLookAtPosition.Enabled = true;
			//        pnlNormalizedDirection.Enabled = true;	
			//        pnlRangeFalloff.Enabled = false; 
			//        pnlAttenuation.Enabled = false; 
			//        pnlConeAngle.Enabled = false; 
			//        CalculateNormalizedDirection(lightNumber, false); 
			//        break;
			//    case LightType.Point:
			//        pnlPosition.Enabled = true;	 
			//        pnlLookAtPosition.Enabled = false;	 
			//        pnlNormalizedDirection.Enabled = false; 
			//        pnlRangeFalloff.Enabled = true; 
			//        pnlAttenuation.Enabled = true; 
			//        pnlConeAngle.Enabled = false; 
			//        break;
			//    case LightType.Spot:
			//        pnlPosition.Enabled = true;	  
			//        pnlLookAtPosition.Enabled = true;
			//        pnlNormalizedDirection.Enabled = true;
			//        pnlRangeFalloff.Enabled = true;
			//        pnlAttenuation.Enabled = true;
			//        pnlConeAngle.Enabled = true;
			//        CalculateNormalizedDirection(lightNumber, false);
			//        break;
			//}

			//set form colors from light
			lblAmbient.BackColor = System.Drawing.Color.FromArgb(l.Ambient.R, l.Ambient.G, l.Ambient.B); lblAmbient.Update();
			lblDiffuse.BackColor = System.Drawing.Color.FromArgb(l.Diffuse.R, l.Diffuse.G, l.Diffuse.B); lblDiffuse.Update();
			lblSpecular.BackColor = System.Drawing.Color.FromArgb(l.Specular.R, l.Specular.G, l.Specular.B); lblSpecular.Update();
			//SetFormColorsFromLight();

			//set form nud colors
			nudAmbientR.Value = l.Ambient.R;
			nudAmbientG.Value = l.Ambient.G;
			nudAmbientB.Value = l.Ambient.B;

			nudDiffuseR.Value = l.Diffuse.R;
			nudDiffuseG.Value = l.Diffuse.G;
			nudDiffuseB.Value = l.Diffuse.B;

			nudSpecularR.Value = l.Specular.R;
			nudSpecularG.Value = l.Specular.G;
			nudSpecularB.Value = l.Specular.B;

			if (pnlPosition.Enabled)
			{
				nudLightPositionX.Value = (decimal)l.Position.X;
				nudLightPositionY.Value = (decimal)l.Position.Y;
				nudLightPositionZ.Value = (decimal)l.Position.Z;
			}
			//if (pnlLookAtPosition.Enabled) //eqv to pnlNormalizedDirection.enabled = true...
			//{
			//    nudNormalizedDirectionX.Value = (decimal)l.Direction.X;
			//    nudNormalizedDirectionY.Value = (decimal)l.Direction.Y;
			//    nudNormalizedDirectionZ.Value = (decimal)l.Direction.Z;
			//}
		
			if ((decimal)l.Range == 0)
			{
				const float reset_range_value = 1f;
				Debug.WriteLine("Code validation: (decimal)Light.Range == 0. Don't set your light range this close. Resetting light to " + reset_range_value);
				l.Range = (float)reset_range_value;
			}
			//we just show the values for one light
			SetNormalizedDirNUDValues(((LightListItem)clbLights.SelectedItem).Light.Direction);

			initializing = false;
        }
		void SetFormColorsFromLight()
		{
			if (!initializing)
			{
				int lightNumber = clbLights.SelectedIndex;
				if (lightNumber < 0)
					return;
				Light l = mDevice.Lights[lightNumber];

				lblAmbient.BackColor = System.Drawing.Color.FromArgb(l.Ambient.R, l.Ambient.G, l.Ambient.B); lblAmbient.Update();
				lblDiffuse.BackColor = System.Drawing.Color.FromArgb(l.Diffuse.R, l.Diffuse.G, l.Diffuse.B); lblDiffuse.Update();
				lblSpecular.BackColor = System.Drawing.Color.FromArgb(l.Specular.R, l.Specular.G, l.Specular.B); lblSpecular.Update();
			}
		}
		[PGBrowsable]//setup the lights and lighting to be used 
        public bool LightingEnabled
        {
            get { return mDevice.RenderState.Lighting; }
            set
            {
                mDevice.RenderState.Lighting = value;
				enabledToolStripMenuItem.Checked = value;
            }
        }

		//private void CalculateNormalizedDirection(int lightNumber, bool updateLight)
		//{
		//    //int lightNumber = clbLights.SelectedIndex;
		//    //if (lightNumber < 0)
		//    //    return;
		//    Light l = mDevice.Lights[lightNumber];

		//    Vector3 direction;

		//    if (pnlPosition.Enabled == false)
		//    {
		//        direction = new Vector3(
		//          (float)(nudLookAtPositionX.Value ),
		//          (float)(nudLookAtPositionY.Value ),
		//          (float)(nudLookAtPositionZ.Value )
		//      );
		//    }
		//    else
		//    {
		//        direction = new Vector3(
		//            (float)(nudLookAtPositionX.Value - nudLightPositionX.Value),
		//            (float)(nudLookAtPositionY.Value - nudLightPositionY.Value),
		//            (float)(nudLookAtPositionZ.Value - nudLightPositionZ.Value)
		//        );
		//    }

		//    direction.Normalize();
		//    nudNormalizedDirectionX.Value = (decimal)l.Direction.X;
		//    nudNormalizedDirectionY.Value = (decimal)l.Direction.Y;
		//    nudNormalizedDirectionZ.Value = (decimal)l.Direction.Z;

		//    RecreateLightCone(SelectedLightIndex);

		//    if (updateLight)
		//    {
		//        l.Direction = direction;
		//        l.Update();
		//    }
		//}

		private void clbLights_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				//initializing = true;
				LoadSelectedLight();  //when user clicks or changes the selected light.
			}
		}

		private void clbLights_ItemCheck(object sender, ItemCheckEventArgs e)
		{
			if (!initializing)
				SetLightEnabled(e.Index, e.NewValue == CheckState.Checked);
		}

		private void SetLightEnabled(int lightNumber, bool enabled)
		{
			if (!initializing)
			{
				Light theLight = mDevice.Lights[lightNumber];
				theLight.Enabled = enabled;
				//if (setCheckState)
				//	clbLights.SetItemChecked(lightNumber, enabled);

				theLight.Update();
				RecreateLightCone(lightNumber);
			}
		}

        private void AmbientRGBChanged()
        {
			Color ambient = Color.FromArgb((int)nudAmbientR.Value, (int)nudAmbientG.Value, (int)nudAmbientB.Value); 
			lblAmbient.BackColor = ambient; lblAmbient.Update();

			foreach (LightListItem l in clbLights.SelectedItems)
			{

				int lightNumber = l.LightIndex;
				if (lightNumber < 0)
					return;


				Light light = mDevice.Lights[lightNumber];
				light.Ambient = ambient;
				light.Update();
			}

        }
        void DiffuseRGBChanged()
        {
			Color diffuse = Color.FromArgb((int)nudDiffuseR.Value, (int)nudDiffuseG.Value, (int)nudDiffuseB.Value);
			lblDiffuse.BackColor = diffuse; lblDiffuse.Update();

			foreach (LightListItem l in clbLights.SelectedItems)
			{

				int lightNumber = l.LightIndex;
				if (lightNumber < 0)
					return;
				Light light = mDevice.Lights[lightNumber];
				light.Diffuse = diffuse;

				light.Update();
			}

        }

        private void SpecularRGBChanged()
        {
			Color specular = Color.FromArgb((int)nudSpecularR.Value, (int)nudSpecularG.Value, (int)nudSpecularB.Value);
			lblSpecular.BackColor = specular; lblSpecular.Update();

			foreach (LightListItem l in clbLights.SelectedItems)
			{
				int lightNumber = l.LightIndex;
				Light light = mDevice.Lights[lightNumber];
				light.Specular = specular;
				light.Update();
			}
        }

		private void nudAmbient_ValueChanged(object sender, EventArgs e)
        {
			if (!initializing)
				AmbientRGBChanged();
        }

        private void nudDiffuse_ValueChanged(object sender, EventArgs e)
        {
			if (!initializing)
				DiffuseRGBChanged();
        }

        private void nudSpecular_ValueChanged(object sender, EventArgs e)
        {
            if (!initializing)
				SpecularRGBChanged();
        }

		private void LightPosition_ValueChanged(int lightNumber)
		{
			if (!initializing)
			{
				if (lightNumber < 0)
					return;
				Light l = mDevice.Lights[lightNumber];
				l.Position = new Vector3(
					(float)nudLightPositionX.Value,
					(float)nudLightPositionY.Value,
					(float)nudLightPositionZ.Value
				);

				//if (SelectedLightType != LightType.Point)
				//{
				//    CalculateNormalizedDirection(lightNumber, false);
				//}
				l.Update();

				RecreateLightCone(lightNumber);
			}
		}

		//private void SetEnabledLightCones()
		//{
		//    List<Light> lightConeLights = ;
		//    SetLightCones(lightConeLights);
		//}

		//private void SetLightCones(List<Light> lightConeLights)
		//{
		//    foreach (Light l in lightConeLights)
		//    {
		//        if (l.Enabled)
		//            SetLightCone(l);
		//    }
		//}

		//private void nudLookAtPoint_ValueChanged(object sender, EventArgs epsilon)
		//{
		//    if (!initializing)
		//    {
		//        foreach (LightListItem l in clbLights.SelectedItems)
		//        {
		//            CalculateNormalizedDirection(l.LightIndex, true);
		//            RecreateLightCone(l.LightIndex);
		//        }
		//    }
		//}

		private void LightingForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (e.CloseReason == CloseReason.UserClosing)
			{
				this.Hide();
				e.Cancel = true;
			}
		}


		private LightType SelectedLightType
		{
			get
			{
				int lightNumber = clbLights.SelectedIndex;
				LightType lightType = ((LightTypeListItem)cboLightType.Items[cboLightType.SelectedIndex]).LightType;
				return lightType;
			}
		}
		private int SelectedLightIndex
		{
			get
			{
				int lightNumber = clbLights.SelectedIndex;
				return lightNumber;
			}
		}
		private void cboLightType_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				int lightNumber = clbLights.SelectedIndex;
				LightType lightType = ((LightTypeListItem)cboLightType.Items[cboLightType.SelectedIndex]).LightType;
				mDevice.Lights[lightNumber].Type = lightType;
				mDevice.Lights[lightNumber].Update();
				LoadSelectedLight();
				Light theLight = mDevice.Lights[lightNumber];
				RecreateLightCone(lightNumber);
			}
		}

//private void SetLightCone(Light theLight)
//{
//    List<Light> lightList = new List<Light>();
//    lightList.Add(theLight);
//    SetLightCone(lightList);
//}

		private void LightingForm_Load(object sender, EventArgs e)
		{
			//put in upper right corner
			//Rectangle workingArea = Screen.GetWorkingArea(new this.);
			//this.Location = new Point(workingArea.Right-this.Width, 0);
			this.Location = new Point(mDirectXWindow.Right - this.Width - 10, mDirectXWindow.Top + 10);
		}

		private void enabledToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (!initializing)
				LightingEnabled = !LightingEnabled;
		}

		//private void chkMoveSelectedLight_CheckedChanged(object sender, EventArgs epsilon)
		//{
		//    if (!initializing)
		//        Mouse.OnlyMouse.MoveCameraPositionWithMouse = !chkMoveSelectedLight.Checked;
		//}

		public static void SetAllLightCones()
		{
			if (lightCones != null)
			{
				for (int i = 0; i < MainProgram.DxDevice.Lights.Count; i++)
					RecreateLightCone(i);
			}
		}
		public static void SetLightCone(int lightIndex)
		{
			RecreateLightCone(lightIndex);
		}

		private void UpdateSelectedLights()
		{
			foreach(LightListItem l in clbLights.SelectedItems)
			{
				l.Light.Update();
			}
		}

		private void nudNormalizedDirectionAll_ValueChanged(object sender, EventArgs e)
		{
			Vector3 dir = new Vector3((float)nudNormalizedDirectionX.Value, (float)nudNormalizedDirectionY.Value, (float)nudNormalizedDirectionZ.Value);
			dir = SetNormalizedDirection(dir);
		}

		private Vector3 SetNormalizedDirection(Vector3 dir)
		{
			if (!initializing)
			{
				foreach (LightListItem l in clbLights.SelectedItems)
				{
					l.Light.Direction = dir;
					l.Light.Update();
					RecreateLightCone(l.LightIndex);
				}
			}
			return dir;
		}

		private void nudDir_ValueChanged(object sender, EventArgs e)
		{
			Vector3 cartesian = Camera.SphericalToCartesian((double)nudDirTheta.Value * Math.PI / 180d, (double)nudDirPhi.Value * Math.PI / 180d, (double)nudDirRho.Value);
			cartesian.Normalize();

			SetNormalizedDirection(cartesian);
			SetNormalizedDirNUDValues(cartesian);
		}

		private void SetNormalizedDirNUDValues(Vector3 direction)
		{
			nudNormalizedDirectionX.Value = (decimal)direction.X;
			nudNormalizedDirectionY.Value = (decimal)direction.Y;
			nudNormalizedDirectionZ.Value = (decimal)direction.Z;
		}

		private void pgViewer_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			if (!initializing)
			{
				UpdateSelectedLights();
				SetAllLightCones();	  //be sure to call update before setting cones since they base thier value off the lights not the controls.
			}	
		}

		private void btnUpdate_Click(object sender, EventArgs e)
		{
			foreach (LightListItem l in clbLights.SelectedItems)
			{
				l.Light.Update();
				lightCones[l.LightIndex].Recreate();
			}
		}

		private void nudLightPositionX_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				foreach (LightListItem l in clbLights.SelectedItems)
				{
					l.Light.Position = new Vector3((float)nudLightPositionX.Value, l.Light.Position.Y, l.Light.Position.Z);
					l.Light.Update();
					lightCones[l.LightIndex].Recreate();
				}
			}
		}

		private void nudLightPositionY_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				foreach (LightListItem l in clbLights.SelectedItems)
				{
					l.Light.Position = new Vector3(l.Light.Position.X, (float)nudLightPositionY.Value, l.Light.Position.Z);
					l.Light.Update();
					lightCones[l.LightIndex].Recreate();
				}
			}
		}

		private void nudLightPositionZ_ValueChanged(object sender, EventArgs e)
		{
			if (!initializing)
			{
				foreach (LightListItem l in clbLights.SelectedItems)
				{
					l.Light.Position = new Vector3(l.Light.Position.X, l.Light.Position.Y, (float)nudLightPositionZ.Value);
					l.Light.Update();
					lightCones[l.LightIndex].Recreate();
				}
			}
		}

	} //end of class
} //end of namespace
